/*
 * Description: performance registry controll utils
 */
#include "registry_utils.h"
#include "Windows.h"
#include "log.h"
#include "performance/utils/perf_utils.h"
#include "../../common/text_utils.h"

using namespace COMMON;
namespace PERF {
constexpr const auto REG_VALUE_MAX_LENGTH = 256;

bool RegistryUtils::GetHKCU(HKEY &hKey)
{
    std::string currentUserSid = PERF_UTILS.GetCurrentUserSid();
    if (currentUserSid.empty()) {
        return false;
    }
    return GetRegKey(HKEY_USERS, currentUserSid.c_str(), hKey);
}

bool RegistryUtils::GetRegKey(const HKEY &hRoot, LPCTSTR subKeyName, HKEY &hKey)
{
    DWORD res = 0;
    res = RegOpenKeyEx(hRoot, subKeyName, 0, KEY_ALL_ACCESS, &hKey);
    if (res != ERROR_SUCCESS) {
        LOG_ERR("[PERF - GetRegKey] - failed, errCode: {}", res);
        return false;
    }
    return true;
}

bool RegistryUtils::CreateRegKey(const HKEY &hKey, LPCSTR subKey, HKEY &hNewKey)
{
    LOG_INFO("[RegistryUtils - CreateRegKey]hKey: {}, subKey: {}, hNewKey: {}",
        TextUtils::HKEYToHexString(hKey), TextUtils::LPCSTRToUTF8(subKey), TextUtils::HKEYToHexString(hNewKey));
    DWORD status =
        RegCreateKeyEx(hKey, subKey, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &hNewKey, nullptr);
    if (status != ERROR_SUCCESS) {
        LOG_ERR("[PERF - CreateRegKey] - failed.");
        return false;
    }
    return true;
}

bool RegistryUtils::GetRegEnumSize(const HKEY &hKey, DWORD &size, DWORD &maxSubKeyLen, DWORD &maxValueNameLen)
{
    if (ERROR_SUCCESS != ::RegQueryInfoKey(hKey, nullptr, nullptr, nullptr, &size, &maxSubKeyLen, nullptr, nullptr,
                                           &maxValueNameLen, nullptr, nullptr, nullptr)) {
        return false;
    }
    return true;
}

bool RegistryUtils::GetRegItemKeyName(const HKEY &hKey, DWORD idx, LPSTR itemKeyName, DWORD &maxSubKeyLen)
{
    if (ERROR_SUCCESS != ::RegEnumKeyEx(hKey, idx, itemKeyName, &maxSubKeyLen, nullptr, nullptr, nullptr, nullptr)) {
        LOG_ERR("[Perf - GetRegItemKeyName] - err: {}", GetLastError());
        return false;
    }
    return true;
}

bool RegistryUtils::QueryRegValue(const HKEY &hRoot, LPCTSTR subKey, LPCTSTR itemKey, OfficeAddInValue &value)
{
    if (value.type == REG_SZ) {
        return QueryRegSZ(hRoot, subKey, itemKey, value.szData);
    } else if (value.type == REG_DWORD) {
        return QueryRegDWORD(hRoot, subKey, itemKey, value.dwData);
    }
    return false;
}

bool RegistryUtils::SetRegValue(const HKEY &hRoot, LPCTSTR subKey, LPCTSTR itemKey, const OfficeAddInValue &value)
{
    if (value.type == REG_SZ) {
        return SetRegSZ(hRoot, subKey, itemKey, value.szData);
    } else if (value.type == REG_DWORD) {
        return SetRegDWORD(hRoot, subKey, itemKey, value.dwData);
    }
    return false;
}

bool RegistryUtils::QueryRegDWORD(const HKEY &hRoot, LPCTSTR subKey, LPCTSTR itemKey, DWORD &itemValue)
{
    HKEY hKey;
    if (!GetRegKey(hRoot, subKey, hKey)) {
        LOG_ERR("[Perf - QueryRegDWORD] - error occurred when GetRegKey: {}", GetLastError());
        return false;
    }
    DWORD dwSize = sizeof itemValue;
    DWORD dwType = REG_DWORD;
    ::RegQueryValueEx(hKey, itemKey, 0, &dwType, (BYTE *)&itemValue, &dwSize);
    SafeCloseHKey(hKey);
    return true;
}

bool RegistryUtils::SetRegDWORD(const HKEY &hRoot, LPCTSTR subKey, LPCTSTR itemKey, DWORD itemValue)
{
    LOG_INFO("[RegistryUtils - SetRegDWORD]hRoot: {}, subKey: {}, itemKey: {}, itemValue:{}",
        TextUtils::HKEYToHexString(hRoot), TextUtils::LPCSTRToUTF8(subKey),
        TextUtils::LPCSTRToUTF8(itemKey), itemValue);
    if (itemValue == REG_DWORD_INVAILD_VALUE) {
        LOG_WARN("[PERF - SetRegDWORD] - met a reg dword invalid value.");
        return false;
    }
    HKEY hKey;
    if (!CreateRegKey(hRoot, subKey, hKey)) {
        LOG_ERR("[PERF - SetRegDWORD] - error occurred when GetRegKey: {}", GetLastError());
        return false;
    }
    if (::RegSetValueEx(hKey, itemKey, 0, REG_DWORD, (BYTE *)&itemValue, sizeof(itemValue)) != ERROR_SUCCESS) {
        LOG_ERR("[PERF - SetRegDWORD] - error occurred when RegSetValueEx: {}", GetLastError());
        SafeCloseHKey(hKey);
        return false;
    }
    SafeCloseHKey(hKey);
    return true;
}

bool RegistryUtils::QueryRegSZ(const HKEY &hRoot, LPCTSTR subKey, LPCTSTR itemKey, std::string &itemValue)
{
    HKEY hKey;
    if (!GetRegKey(hRoot, subKey, hKey)) {
        LOG_ERR("[PERF - QueryRegSZ] - error occurred when GetRegKey: {}", GetLastError());
        return false;
    }
    TCHAR itemValueCStr[REG_VALUE_MAX_LENGTH] = { 0 };
    DWORD dwSize = REG_VALUE_MAX_LENGTH;
    DWORD status = RegGetValue(hKey, nullptr, itemKey, RRF_RT_REG_SZ, nullptr, (BYTE *)&itemValueCStr, &dwSize);
    if (status != ERROR_SUCCESS) {
        LOG_ERR("[PERF - QueryRegSZ] - RegGetValue failed, status: {}", status);
        return false;
    }
    itemValue = std::string(itemValueCStr);
    SafeCloseHKey(hKey);
    return true;
}

bool RegistryUtils::SetRegSZ(const HKEY &hRoot, LPCTSTR subKey, LPCTSTR itemKey, const std::string &itemValue)
{
    LOG_INFO("[RegistryUtils - SetRegSZ]hRoot: {}, subKey: {}, itemKey: {}, itemValue:{}",
        TextUtils::HKEYToHexString(hRoot),
        TextUtils::LPCSTRToUTF8(subKey), TextUtils::LPCSTRToUTF8(itemKey), itemValue);
    if (itemValue == REG_SZ_INVAILD_VALUE) {
        LOG_WARN("[PERF - SetRegSZ] - met a reg sz invalid value.");
        return false;
    }
    HKEY hKey;
    if (!CreateRegKey(hRoot, subKey, hKey)) {
        LOG_ERR("[PERF - SetRegSZ] - error occurred when GetRegKey: {}", GetLastError());
        return false;
    }
    TCHAR itemValueCStr[REG_VALUE_MAX_LENGTH] = { 0 };
    if (strcpy_s(itemValueCStr, itemValue.c_str()) != ERROR_SUCCESS) {
        LOG_ERR("[PERF - SetRegSZ] - strcpy failed");
        return false;
    }
    if (::RegSetValueEx(hKey, itemKey, 0, REG_SZ, (BYTE *)&itemValueCStr, itemValue.size() + sizeof TCHAR) !=
        ERROR_SUCCESS) {
        LOG_ERR("[PERF - SetRegSZ] - error occurred: {}", GetLastError());
        SafeCloseHKey(hKey);
        return false;
    }
    SafeCloseHKey(hKey);
    return true;
}

bool RegistryUtils::DeleteRegKey(const HKEY &hKey, LPCSTR subKey)
{
    LOG_INFO("[RegistryUtils - DeleteRegKey]hKey: {}, subKey: {}",
        TextUtils::HKEYToHexString(hKey), TextUtils::LPCSTRToUTF8(subKey));
    DWORD status = RegDeleteKey(hKey, subKey);
    if (status != ERROR_SUCCESS) {
        LOG_ERR("[PERF - DeleteRegKey] - failed");
        return false;
    }
    return true;
}

void RegistryUtils::SafeCloseHKey(HKEY &hKey)
{
    if (hKey != nullptr) {
        ::RegCloseKey(hKey);
        hKey = nullptr;
    }
}
}  // namespace PERF